package com.iakuil.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.iakuil.app.common.core.enums.Results;
import com.iakuil.app.common.core.exception.BusinessException;
import com.iakuil.app.common.core.util.BeanUtils;
import com.iakuil.app.repository.dao.BookMapper;
import com.iakuil.app.domain.dto.AuthorDto;
import com.iakuil.app.domain.dto.BookDto;
import com.iakuil.app.domain.dto.PressDto;
import com.iakuil.app.domain.dto.event.AuthorRemovedEvent;
import com.iakuil.app.domain.dto.event.PressRemovedEvent;
import com.iakuil.app.domain.dto.query.BookQuery;
import com.iakuil.app.domain.dto.vo.BookInfoVo;
import com.iakuil.app.domain.entity.Author;
import com.iakuil.app.domain.entity.Book;
import com.iakuil.app.service.AuthorService;
import com.iakuil.app.service.BookService;
import com.iakuil.app.service.PressService;
import com.iakuil.app.service.base.BaseServiceImpl;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 书籍 服务实现类
 *
 * <p>涉及级联操作，所以没有使用一二级缓存
 *
 * @author Kai
 * @since 2023-11-28
 */
@Service
public class BookServiceImpl extends BaseServiceImpl<BookMapper, Book> implements BookService {

    private final AuthorService authorService;

    private final PressService pressService;

    public BookServiceImpl(AuthorService authorService, PressService pressService) {
        this.authorService = authorService;
        this.pressService = pressService;
    }

    @Override
    public BookInfoVo getDetailsById(Long id) {
        Book entity = Optional.ofNullable(this.getById(id)).orElseThrow(() -> new BusinessException(Results.NOT_FOUND));

        // 简单级联查询，直接拼装VO
        BookInfoVo vo = BeanUtils.copy(entity, BookInfoVo.class);
        Optional.ofNullable(entity.getPressId()).map(pressService::getById).ifPresent(press -> vo.setPress(BeanUtils.copy(press, PressDto.class)));
        Optional.ofNullable(entity.getAuthorIds()).map(authorService::listByIds).ifPresent(authors ->
            vo.setAuthor(authors.stream().map(item -> BeanUtils.copy(item, AuthorDto.class)).toArray(AuthorDto[]::new))
        );

        return vo;
    }

    @Override
    public List<BookDto> listByCondition(BookQuery query) {
        List<Book> entities = baseMapper.selectList(new QueryWrapper<Book>()
            .select(getColumns(query.getFields()))
            .lambda()
            .likeRight(query.getTitle() != null, Book::getTitle, query.getTitle())
            .eq(query.getIsbn() != null, Book::getIsbn, query.getIsbn())
            .eq(query.getPressId() != null, Book::getPressId, query.getPressId())
            // 设计表时，使用ID列表字段关联（逗号分隔），从而规避联表查询
            .apply(query.getAuthorId() != null, "FIND_IN_SET ('" + query.getAuthorId() + "', author_ids)")
            .orderByDesc(Book::getCreateTime));

        // 复杂级联查询，使用一二级缓存代替LEFT JOIN
        return entities.stream().map(entity -> {
            BookDto dto = BeanUtils.copy(entity, BookDto.class);
            Optional.ofNullable(entity.getPressId()).map(pressService::getById).ifPresent(press -> dto.setPressName(press.getName()));
            // 注意：此处使用getById而不是listByIds方法
            Optional.ofNullable(entity.getAuthorIds()).map(ids ->
                Arrays.stream(ids).map(authorService::getById).collect(Collectors.toList())
            ).ifPresent(authors ->
                dto.setAuthorName(authors.stream().filter(Objects::nonNull).map(Author::getName).collect(Collectors.joining("、")))
            );

            return dto;
        }).collect(Collectors.toList());
    }

    @Async
    @EventListener
    public void onRemoved(PressRemovedEvent event) {
        Long[] ids = event.getIds();
        if (ids != null) {
            // 级联删除出版社，基于事件机制，解决了部分循环调用问题
            baseMapper.update(new UpdateWrapper<Book>().lambda().set(Book::getPressId, 0L).in(Book::getPressId, ids));
        }
    }

    @Async
    @EventListener
    public void onRemoved(AuthorRemovedEvent event) {
        Long[] ids = event.getIds();
        if (ids != null) {
            List<Book> entities = listByIds(ids);

            // 级联删除作者
            for (Book entity : entities) {
                Long[] authorIds = entity.getAuthorIds();
                if (authorIds != null) {
                    baseMapper.update(new UpdateWrapper<Book>().lambda()
                        .set(Book::getAuthorIds, ArrayUtils.removeElement(authorIds, ids))
                        .eq(Book::getId, entity.getId())
                    );
                }
            }
        }
    }
}
